/*
   Copyright 2022 cmanlh

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/**
 * date input
 */
import JscConfig from './config.js';
import JscDatabind from './databind.js';
import JscDate from './date.js';
export default class JscInputDate extends JscDatabind {
    static get observedAttributes() { return ['max', 'min', 'required', 'readonly', 'disabled', 'placeholder', 'value', 'jscbind'] }

    #value;
    #input = undefined;
    #required = false;
    #jscBind = null;
    #max = null;
    #min = null;
    #cancelHandler = null;
    #container = undefined;
    #displayYear = null;
    #displayMonth = null;
    #displayDay = null;

    constructor() {
        super();
        this.#init();
        this.#render();
    }

    connectedCallback() {
        this.#bindEventForInput();
    }

    disconnectedCallback() {
        if (null != this.#cancelHandler) {
            document.removeEventListener('click', this.#cancelHandler);
        }
    }

    attributeChangedCallback(name, oldValue, newValue) {
        switch (name) {
            case 'value': {
                if (oldValue != newValue) {
                    this.value = newValue;
                }
                break;
            }
            case 'required': {
                if (oldValue != newValue) {
                    if (null == newValue) {
                        this.#required = false;
                    } else {
                        this.#required == newValue;
                    }
                }
                break;
            }
            case 'disabled': {
                if (oldValue != newValue) {
                    if (newValue || '' == newValue) {
                        this.#input.setAttribute('disabled', true);
                    } else {
                        this.#input.removeAttribute('disabled');
                    }
                }
                break;
            }
            case 'jscbind': {
                if (oldValue != newValue) {
                    this.#jscBind = newValue;
                }
                break;
            }
        }
    }

    bindRefresh(bindStamp) {
        if (super.bindRefresh(bindStamp)) {
            if (null != this.bind && null != this.#jscBind) {
                let _newValue = this.fetchBindValue(this.#jscBind);
                if (null == _newValue) {
                    this.#input.value = null;
                } else {
                    let newValue = JscDate.toDate(_newValue);
                    this.#value = newValue.getTime();
                    this.#input.value = newValue.format();
                }
            }
        }
    }

    set value(val) {
        let self = this;
        let oldValue = self.#value;
        let newValue = JscDate.toDate(val);
        if (oldValue == newValue.getTime()) {
            return;
        }
        self.#value = newValue.getTime();
        if (null == self.#value) {
            self.#input.value = null;
        } else {
            self.#input.value = newValue.format();
        }
        this.updateBindValue(this.#jscBind, this.#value);
    }

    get value() {
        return this.#value;
    }

    #init() {
        let required = this.getAttribute('required');
        if (null == required) {
            this.#required = false;
        } else {
            this.#required = true;
        }

        this.#jscBind = this.getAttribute('jscbind');
        let max = this.getAttribute('max');
        if (null != max) {
            this.#max = JscDate.toDate(max);
        }
        let min = this.getAttribute('min');
        if (null != min) {
            this.#min = JscDate.toDate(min);
        }
    }

    #render() {
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
        <link rel='stylesheet' type='text/css' href='${JscConfig.cssUrl}'/>
        <input type='text' class='inputDate' readonly>
        <span title='清空' class='inputDateClear'></span>
        <div class='inputDateContainer' style='display:none;'>
            <div class='inputDateCtrlPanel'>
                <div class='inputDatePreMonth inputDateArrow'>&nbsp;</div>
                <div class='inputDateMonthValue inputDateSelector inputDateSelectorInit'>十二月</div>
                <div class='inputDateYearValue inputDateSelector inputDateSelectorInit'>2022</div>
                <div class='inputDateNextMonth inputDateArrow'>&nbsp;</div>
            </div>
            <div class='inputDateBody'>
                <div class='inputDateMonthSelector' style='display:none;'>
                    <div><span month-value='0'>一月</span><span month-value='1'>二月</span><span month-value='2'>三月</span><span month-value='3'>四月</span></div>
                    <div><span month-value='4'>五月</span><span month-value='5'>六月</span><span month-value='6'>七月</span><span month-value='7'>八月</span></div>
                    <div><span month-value='8'>九月</span><span month-value='9'>十月</span><span month-value='10'>十一月</span><span month-value='11'>十二月</span></div>
                </div>
                <div class='inputDateYearSelector' style='display:none;'>
                    <div><span></span><span></span><span></span><span></span></div>
                    <div><span></span><span></span><span></span><span></span></div>
                    <div><span></span><span></span><span></span><span></span></div>
                    <div><span></span><span></span><span></span><span></span></div>
                    <div><span></span><span></span><span></span><span></span></div>
                    <div class='inputDateYearCtrlBar'><button class='inputDatePreYear inputDateArrow'></button><button class='inputDateNextYear inputDateArrow'></button></div>
                </div>
                <div class='inputDateDaySelector'><table>
                    <thead>
                        <tr><th>日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th></tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table></div>
            </div>
        </div>
        `;
        this.#input = this.shadowRoot.querySelector('input');
        this.#container = this.shadowRoot.querySelector('div');

        if (this.getAttribute('placeholder')) {
            this.#input.setAttribute('placeholder', this.getAttribute('placeholder'));
        }

        if (this.getAttribute('disabled')) {
            this.#input.setAttribute('disabled', true);
        }
    }

    #renderCalender(newDate) {
        let self = this;
        let currentDate = new Date();
        if (null != newDate) {
            currentDate = JscDate.toDate(newDate);
        }

        let reRenderDays = false;
        let year = currentDate.getFullYear(), month = currentDate.getMonth(), day = currentDate.getDate();
        if (self.#displayYear != year) {
            self.#displayYear = year;
            self.shadowRoot.querySelector('.inputDateYearValue').textContent = self.#displayYear;

            reRenderDays = true;
        }

        if (self.#displayMonth != month) {
            self.#displayMonth = month;
            self.shadowRoot.querySelector('.inputDateMonthValue').textContent = self.#monthConvertor(self.#displayMonth);

            reRenderDays = true;
        }

        if (reRenderDays) {
            let date = new Date();
            date.setFullYear(year);
            date.setMonth(month);
            date.setDate(1);

            let preDate = date.plusDays(-1);
            let preMaxDays = preDate.maxDaysInThisMonth();
            let preYear = preDate.getFullYear(), preMonth = preDate.getMonth();
            let currentMaxDays = date.maxDaysInThisMonth();
            let week = date.getDay();

            let tbody = document.createElement('tbody');
            let firstRow = document.createElement('tr');
            for (let i = preMaxDays - week + 1; i <= preMaxDays; i++) {
                let toSettingDate = JscDate.from(preYear, preMonth, i);
                if ((null != self.#min && self.#min.compareTo(toSettingDate) > 0) || (null != self.#max && self.#max.compareTo(toSettingDate) < 0)) {
                    firstRow.append(self.#createDayElement(preYear, preMonth, i, false, false, true));
                } else {
                    firstRow.append(self.#createDayElement(preYear, preMonth, i, false, false, false));
                }
            }

            let tmpDay = 1;
            for (let i = week; i <= 6; i++) {
                let toSettingDate = JscDate.from(year, month, tmpDay);
                if ((null != self.#min && self.#min.compareTo(toSettingDate) > 0) || (null != self.#max && self.#max.compareTo(toSettingDate) < 0)) {
                    firstRow.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, true));
                } else {
                    firstRow.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, false));
                }
                tmpDay += 1;
            }
            tbody.append(firstRow);

            let weeks = (currentMaxDays - tmpDay + 1) / 7;
            for (let i = 1; i <= weeks; i++) {
                let row = document.createElement('tr');
                for (let i = 0; i <= 6; i++) {
                    let toSettingDate = JscDate.from(year, month, tmpDay);
                    if ((null != self.#min && self.#min.compareTo(toSettingDate) > 0) || (null != self.#max && self.#max.compareTo(toSettingDate) < 0)) {
                        row.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, true));
                    } else {
                        row.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, false));
                    }
                    tmpDay += 1;
                }
                tbody.append(row);
            }

            if ((currentMaxDays - tmpDay + 1) % 7 != 0) {
                let row = document.createElement('tr');
                let lastWeek = 0;
                for (let i = tmpDay; i <= currentMaxDays; i++) {
                    let toSettingDate = JscDate.from(year, month, tmpDay);
                    if ((null != self.#min && self.#min.compareTo(toSettingDate) > 0) || (null != self.#max && self.#max.compareTo(toSettingDate) < 0)) {
                        row.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, true));
                    } else {
                        row.append(self.#createDayElement(year, month, tmpDay, true, tmpDay == day, false));
                    }
                    tmpDay += 1;
                    lastWeek++;
                }

                date.setDate(currentMaxDays);
                let nextDate = date.plusDays(1);
                let nextYear = nextDate.getFullYear(), nextMonth = nextDate.getMonth();
                for (let i = lastWeek; i <= 6; i++) {
                    let toSettingDate = JscDate.from(nextYear, nextMonth, i - lastWeek + 1);
                    if ((null != self.#min && self.#min.compareTo(toSettingDate) > 0) || (null != self.#max && self.#max.compareTo(toSettingDate) < 0)) {
                        row.append(self.#createDayElement(nextYear, nextMonth, i - lastWeek + 1, false, false, true));
                    } else {
                        row.append(self.#createDayElement(nextYear, nextMonth, i - lastWeek + 1, false, false, false));
                    }
                }
                tbody.append(row);
            }
            self.#displayDay = day;

            self.shadowRoot.querySelector('tbody').remove();
            self.shadowRoot.querySelector('table').append(tbody);
        } else {
            if (self.#displayDay != day) {
                let oldCurrentDay = self.shadowRoot.querySelector('.inputDateCurrentDay');
                if (oldCurrentDay) {
                    oldCurrentDay.classList.remove('inputDateCurrentDay');
                }
                self.shadowRoot.querySelector('.inputDateCurrentMonthDay[data-day="'.concat(day).concat('"]')).classList.add('inputDateCurrentDay');
                self.#displayDay = day;
            }
        }
    }

    #renderYearSelector(selector, min) {
        let self = this;
        let spans = selector.querySelectorAll('span');
        let year = min;

        for (let i = 0; i < spans.length; i++) {
            if ((null != self.#min && self.#min.getFullYear() <= year) && (null != self.#max && self.#max.getFullYear() >= year)) {
                spans[i].classList.add('inputDateEnabledYear');
                spans[i].classList.remove('inputDateDisabledYear');
            } else {
                spans[i].classList.remove('inputDateEnabledYear');
                spans[i].classList.add('inputDateDisabledYear');
            }
            spans[i].setAttribute('year-value', year);
            spans[i].textContent = year;

            year++;
        }
    }

    #createDayElement(year, month, day, isCurrentMonth, isCurrentDay, isDisabled) {
        let dayElement = document.createElement('td');
        dayElement.textContent = day;
        if (isDisabled) {
            dayElement.classList.add('inputDateDisabledDay');
        } else {
            if (isCurrentMonth) {
                dayElement.classList.add('inputDateCurrentMonthDay');
            } else {
                dayElement.classList.add('inputDateNonMonthDay');
            }
            if (isCurrentDay) {
                dayElement.classList.add('inputDateCurrentDay');
            }
        }
        dayElement.setAttribute('data-year', year);
        dayElement.setAttribute('data-month', month);
        dayElement.setAttribute('data-day', day);

        return dayElement;
    }

    #bindEventForInput() {
        let self = this;

        let inputDateDaySelector = self.shadowRoot.querySelector('.inputDateDaySelector');
        let inputDateMonthValue = self.shadowRoot.querySelector('.inputDateMonthValue');
        let inputDateMonthSelector = self.shadowRoot.querySelector('.inputDateMonthSelector');
        let inputDateYearValue = self.shadowRoot.querySelector('.inputDateYearValue');
        let inputDateYearSelector = self.shadowRoot.querySelector('.inputDateYearSelector');

        self.#input.addEventListener('click', function (e) {
            if ('block' == self.#container.style.display) {
                self.#container.style.display = 'none';
                return
            }

            self.#container.style.display = 'block';
            let inputStyle = window.getComputedStyle(self.#input);
            self.#container.style.top = self.#input.offsetTop + parseFloat(inputStyle.height) + parseFloat(inputStyle.borderTopWidth) + parseFloat(inputStyle.borderBottomWidth) + 1 + 'px';
            self.#container.style.left = self.#input.offsetLeft + 'px';

            inputDateMonthSelector.style.display = 'none';
            inputDateYearSelector.style.display = 'none';
            inputDateDaySelector.style.display = 'block';

            inputDateMonthValue.classList.remove('inputDateSelectorActive');
            inputDateMonthValue.classList.add('inputDateSelectorInit');
            inputDateYearValue.classList.remove('inputDateSelectorActive');
            inputDateYearValue.classList.add('inputDateSelectorInit');

            self.#renderCalender(self.#value);
        });

        inputDateDaySelector.addEventListener('click', function (e) {
            let dayTd = e.target;
            if ('TD' == dayTd.tagName && !dayTd.classList.contains('inputDateDisabledDay')) {
                let date = new Date('2020/01/01');
                date.setFullYear(dayTd.getAttribute('data-year'));
                date.setMonth(dayTd.getAttribute('data-month'));
                date.setDate(dayTd.getAttribute('data-day'));

                self.value = date;
                self.#container.style.display = 'none';
            }
        });

        let wheelTimestamp = 0;
        inputDateDaySelector.addEventListener('wheel', function (e) {
            let newWheelTimestamp = Date.now();

            if (newWheelTimestamp - wheelTimestamp > 200) {
                wheelTimestamp = newWheelTimestamp;
                let date = new Date('2022/01/01');
                if (null != self.#displayYear) {
                    date.setFullYear(self.#displayYear);
                }
                if (null != self.#displayMonth) {
                    date.setMonth(self.#displayMonth);
                }
                if (null != self.#displayDay) {
                    date.setDate(self.#displayDay);
                }
                if (e.deltaY > 0) {
                    self.#renderCalender(date.plusMonths(1));
                } else {
                    self.#renderCalender(date.plusMonths(-1));
                }
            }
            e.preventDefault();
        });

        inputDateMonthValue.addEventListener('click', function (e) {
            if ('block' == inputDateMonthSelector.style.display) {
                inputDateMonthSelector.style.display = 'none';
                inputDateYearSelector.style.display = 'none';
                inputDateDaySelector.style.display = 'block';

                inputDateMonthValue.classList.remove('inputDateSelectorActive');
                inputDateMonthValue.classList.add('inputDateSelectorInit');
                return
            }
            inputDateMonthValue.classList.add('inputDateSelectorActive');
            inputDateMonthValue.classList.remove('inputDateSelectorInit');
            inputDateYearValue.classList.remove('inputDateSelectorActive');
            inputDateYearValue.classList.add('inputDateSelectorInit');

            inputDateMonthSelector.style.display = 'block';
            inputDateYearSelector.style.display = 'none';
            inputDateDaySelector.style.display = 'none';
        });

        inputDateMonthSelector.addEventListener('click', function (e) {
            let monthSpan = e.target;
            if ('SPAN' == monthSpan.tagName) {
                let date = new Date('2020/01/01');
                date.setFullYear(self.#displayYear);
                date.setMonth(monthSpan.getAttribute('month-value'));
                date.setDate(self.#displayDay);

                self.#renderCalender(date);
                inputDateMonthSelector.style.display = 'none';
                inputDateDaySelector.style.display = 'block';

                inputDateMonthValue.classList.remove('inputDateSelectorActive');
                inputDateMonthValue.classList.add('inputDateSelectorInit');
            }
        });

        inputDateMonthSelector.addEventListener('wheel', function (e) {
            e.preventDefault();
        });

        let currentMinYear = 0;
        inputDateYearValue.addEventListener('click', function (e) {
            if ('block' == inputDateYearSelector.style.display) {
                inputDateMonthSelector.style.display = 'none';
                inputDateYearSelector.style.display = 'none';
                inputDateDaySelector.style.display = 'block';

                inputDateYearValue.classList.remove('inputDateSelectorActive');
                inputDateYearValue.classList.add('inputDateSelectorInit');
                return
            }

            inputDateMonthValue.classList.remove('inputDateSelectorActive');
            inputDateMonthValue.classList.add('inputDateSelectorInit');
            inputDateYearValue.classList.add('inputDateSelectorActive');
            inputDateYearValue.classList.remove('inputDateSelectorInit');

            inputDateMonthSelector.style.display = 'none';
            inputDateYearSelector.style.display = 'block';
            inputDateDaySelector.style.display = 'none';

            if (self.#displayYear - 10 != currentMinYear) {
                currentMinYear = self.#displayYear - 10;

                self.#renderYearSelector(inputDateYearSelector, currentMinYear);
            }
        });

        inputDateYearSelector.addEventListener('click', function (e) {
            let yearSpan = e.target;
            if ('SPAN' == yearSpan.tagName) {
                let date = new Date('2020/01/01');
                date.setFullYear(yearSpan.getAttribute('year-value'));
                date.setMonth(self.#displayMonth);
                date.setDate(self.#displayDay);

                self.#renderCalender(date);
                inputDateYearSelector.style.display = 'none';
                inputDateDaySelector.style.display = 'block';

                inputDateYearValue.classList.remove('inputDateSelectorActive');
                inputDateYearValue.classList.add('inputDateSelectorInit');
            }
        });

        let yearWheelTimestamp = 0;
        inputDateYearSelector.addEventListener('wheel', function (e) {
            let newWheelTimestamp = Date.now();

            if (newWheelTimestamp - yearWheelTimestamp > 200) {
                yearWheelTimestamp = newWheelTimestamp;

                if (e.deltaY > 0) {
                    currentMinYear += 20;
                } else {
                    currentMinYear -= 20;
                }
                self.#renderYearSelector(inputDateYearSelector, currentMinYear);
            }
            e.preventDefault();
        });

        self.#container.querySelector('.inputDatePreMonth').addEventListener('click', function (e) {
            let date = new Date('2020/01/01');
            date.setFullYear(self.#displayYear);
            date.setMonth(self.#displayMonth - 1);
            date.setDate(self.#displayDay);

            self.#renderCalender(date);

        });

        self.#container.querySelector('.inputDateNextMonth').addEventListener('click', function (e) {
            let date = new Date('2020/01/01');
            date.setFullYear(self.#displayYear);
            date.setMonth(self.#displayMonth + 1);
            date.setDate(self.#displayDay);

            self.#renderCalender(date);

        });

        self.#container.querySelector('.inputDatePreYear').addEventListener('click', function (e) {
            currentMinYear -= 20;

            self.#renderYearSelector(inputDateYearSelector, currentMinYear);

        });

        self.#container.querySelector('.inputDateNextYear').addEventListener('click', function (e) {
            currentMinYear += 20;

            self.#renderYearSelector(inputDateYearSelector, currentMinYear);
        });

        self.#cancelHandler = function (e) {
            if (e.target != self && 'block' == self.#container.style.display) {
                self.#container.style.display = 'none';
                return
            }
        };
        document.addEventListener('click', self.#cancelHandler);
    }

    checkValidity() {
        let errMsg = '';
        if (this.#required && (undefined == this.#value || null == this.#value || '' == this.#value)) {
            errMsg = '该字段不能为空';
        }

        this.#input.setCustomValidity(errMsg);
        this.#input.reportValidity();

        if ('' === errMsg) {
            return true;
        } else {
            return false;
        }
    }

    #monthConvertor(num) {
        switch (num) {
            case 0: {
                return '一月';
            }
            case 1: {
                return '二月';
            }
            case 2: {
                return '三月';
            }
            case 3: {
                return '四月';
            }
            case 4: {
                return '五月';
            }
            case 5: {
                return '六月';
            }
            case 6: {
                return '七月';
            }
            case 7: {
                return '八月';
            }
            case 8: {
                return '九月';
            }
            case 9: {
                return '十月';
            }
            case 10: {
                return '十一月';
            }
            case 11: {
                return '十二月';
            }
        };
    }
}

if (!customElements.get('jsc-input-date')) {
    window.customElements.define("jsc-input-date", JscInputDate);
}